package com.hdp.kotlin_samples.middle

/**
 * author:hdp
 * on:2019/6/13:10:10
 * desc:
 * 扩展函数和属性
 * 扩展函数和属性是Kotlin非常方便实用的一个功能
 *
 * 1、扩展函数的定义
 *     fun 类名.方法名()：通过类名后面跟：.方法名 实现
 * 2、扩展字段的定义
 *      类名.字段名：通过类名后面跟：.字段名 实现
 *
 * 3、扩展函数的优势：
 *      1、直接通过对象+.访问，可以返回自身，不会破坏链接调用结构
 *      2、扩展函数IDE已斜体黄字显示
 *      3、扩展属性IDE以斜体显示
 *
 * 4、扩展函数的实现原理
 *      1、所有的扩展函数和扩展属性都会被编译成静态一个方法，这个方法的第一个参数就是被扩展的对象，
 *      然后才是其它各个参数。
 *      2、val的扩展属性会生成对应的getXXX方法，var的扩展属性会生成getXXX和setXXX方法
 *
 * 5、扩展函数和成员函数的区别：
 *      1、扩展函数和成员函数使用方式类似，可以直接访问被扩展类的方法和属性。
 *      2、不能像成员函数一样直接访问内部私有函数和属性。(原理: 扩展函数访问实际是类的对象访问）
 *      3、扩展函数实际上是一个静态函数是处于类的外部，而成员函数则是类的内部函数
 *      4、父类成员函数可以被子类重写，而扩展函数则不行
 *
 * 6、扩展函数是通过静态解析执行的（编译期就决定了要调用的静态方法）：
 *      1、静态解析：根据调用对象、方法名找到拓展函数，转换为函数调用。
 *      2、如：str.lastChar()执行流程：
 *          1）检查str类型（发现为String类型）
 *          2）检查String是否定义了lastChar()成员方法，如果定义了，编译直接通过
 *          3）如果String没定义lastChar()方法，kotlin开始查找程序是否有为String类扩展了lastChar()方法
 *              （即是否有fun String.lastChar()），如果有定义该扩展方法，会调用该扩展方法；
 *          4）既没定义lastChar()成员方法也没定义扩展方法，编译自然不通过。
 *
 * 7、带接受者的匿名扩展函数：
 *      1、扩展方法（fun 类名.方法名()）去掉方法名就是所谓的带接收者的匿名扩展函数，
 *          接收者就是类本身，形如：fun Int.() : Int。
 */

fun main() {
//    testExtFunction()
//    testExtField()
    testExtIfCanOverride()
}

/**
 * String类中，我们想获取最后一个字符，String中没有这样的直接函数，你可以用.后声明这样一个扩展函数：
 */

/*
* 扩展函数
* */
fun String.lastChar(): Char = this[this.length - 1]

/*
* 测试
* */
fun testExtFunction() {
    println("======testExtensionField========")
    val str = "test extension fun";
    //直接调用扩展函数
    println("扩展函数：${str.lastChar()}")
}


/*
* 扩展属性 lastChar获取String的最后一个字符
* */
val String.lastChar: Char
    get() = get(length - 1)

//扩展字段
fun testExtField() {
    println("======testExtensionField========")
    val s = "abc"
    println("扩展属性：" + s.lastChar)
}


//=========测试父类的扩展函数是否能被子类重写=========
/**
 * 父类
 */
open class ExtensionTest

/**
 * 子类
 */
class ExtensionSubTest : ExtensionTest()

/**
 * 父类扩展一个test方法
 */
fun ExtensionTest.test() = println("父类扩展方法")

/**
 * 子类扩展一个test方法
 */
fun ExtensionSubTest.test() = println("子类扩展方法")

/**
 * 测试父类的扩展函数是否能被子类重写
 */
fun testExtIfCanOverride() {
    val father: ExtensionTest = ExtensionTest()
    father.test()//调用父类扩展方法
    val child1: ExtensionTest = ExtensionSubTest()
    child1.test()//引用类型为父类类型，编译时静态调用的还是父类的扩展方法
    val child2: ExtensionSubTest = ExtensionSubTest()
    child2.test()//此时才是调用子类的扩展方法
}

